<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <title>面向对象</title>
</head>
<body>

<script>

/**
 * 类的声明：
 * 1.构造函数声明
 * 2.ES6语法声明
 */

function Animal(name) {
  this.name = name
}

/**
 * ES6语法声明类
 */

class Animal2 {
  constructor(name) {
    this.name = name
  }
}

/**
 * 实例化
 */
console.log(new Animal(), new Animal2())

/**
 * 继承的基本原理：通过原型链实现继承
 */

/**
 *  1.借助构造函数实现继承(通过call方法实现继承)
 *  缺点：实现部分继承，父级构造函数Parent1的原型对象上面的属性/方法，无法被Child1继承
 */
function Parent1() {
  this.name = 'parent1'
  this.play = [1, 2, 3]
}

Parent1.prototype.say = function () {
  console.log('say something')
}

function Child1() {
  Parent1.call(this)
  this.type = 'child1'
}

let s = new Child1()
let s0 = new Child1()
s.play.push(4)

// console.log(s, s0, s.say())  // say会报错，找不到该方法

/**
 *  2.借助原型链实现继承（弥补构造函数继承的缺陷）
 */

function Parent2() {
  this.name = 'parent2'
  this.play = [1, 2, 3]
}

Parent2.prototype.say = function () {
  return 'say something'
}

function Child2() {
  this.type = 'child2'
}

Child2.prototype = new Parent2()

let s1 = new Child2()
let s2 = new Child2()
s1.play.push(4)
console.log(s1.play, s2.play, new Child2().say())


/**
 *  3.组合方式
 *  缺点：父类构造函数被执行2次。
 *  在写之类原型链的时候，也就是Child3.prototype的时候，实例化了父类的实例对象。
 *  在每一次实例化的时候，父类的构造函数都会被执行二次。也就是call的时候和Child3.prototype = new Parent3()各执行一次
 */

function Parent3() {
  this.name = 'parent3'
  this.play = [1, 2, 3]
}

Parent3.prototype.say = function () {
  return 'say something'
}

function Child3() {
  Parent3.call(this)
  this.type = 'child3'
}

Child3.prototype = new Parent3()

let s3 = new Child3()
let s4 = new Child3()
s3.play.push(4)
console.log(s3.play, s4.play, new Child3().say())

/**
 * 组合继承的优化一
 */

function Parent4() {
  this.name = 'parent4'
  this.play = [1, 2, 3]
}

function Child4() {
  Parent4.call(this)
  this.type = 'child4'
}

Child4.prototype = Parent4.prototype
let s5 = new Child4()
let s6 = new Child4()
s5.play.push(4)
console.log(s5, s6)

// console.log(s5 instanceof Child4, s5 instanceof Parent4)
// console.log(s5.__proto__.constructor === Child4)  // false
// console.log(s5.__proto__.constructor === Parent4) // true

/**
 * 组合继承的优化二
 */
function Parent5() {
  this.name = 'parent5'
  this.play = [1, 2, 3]
}

function Child5() {
  Parent5.call(this)
  this.type = 'child5'
}

Child5.prototype = Object.create(Parent5.prototype)
Child5.prototype.constructor = Child5
let s7 = new Child5()

console.log(s7 instanceof Child5, s7 instanceof Parent5)
console.log(s7.__proto__.constructor === Child5)  // true
console.log(s7.__proto__.constructor === Parent5) // false

</script>

</body>
</html>
